#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
文件名称: analysis_api.py
完整路径: backend/api/analysis_api.py
功能说明:
    统计报表接口，支持固定模板 (template1/按年龄段, template2/按性别) 和自定义查询 (query_mode="custom")。
    固定模板下：
      - template1 按年龄段分组，分组名称设置为"年龄"；
      - template2 按性别分组，分组名称设置为"性别"。
    数据查询结果中，每行第一列显示分组值，中间为各统计指标以及其它类的“数量”和“占比”，最后一列为统计总数。
    合计行中，“数量”列直接累加，各“占比”列按对应数量与总体统计总数计算。
修改要点:
1. 修正合计行累加逻辑：循环范围扩展到最后一列，并对“占比”列重新计算。
2. 固定模板模式下显式设置 group_title，以确保第一列表头不为空。
3. 对于自定义查询模式，如果统计指标未选择子选项则返回错误提示。
4. 当分组字段为多选且勾选了子选项，后端需对该字段执行 col.in_(val) 过滤，仅统计用户选择的分组值。
5. 在 FIELD_DISPLAY_MAPPING 中增加 "grade": "年级"，保证第一列显示中文“年级”。
6. 针对高级条件解析中数值字段，区分单值与区间查询；支持多个区间（每个区间以字典形式传入），以及单一数值（若 dict 中只有 min 或 max），并对区间标签进行格式化（整数显示为整数，浮点数保留小数），同时在累加过程中将 None 转换为 0。
7. 扩展 METRIC_CONFIG 配置，添加 complete_fields 列表中所有字段的配置。
"""

# pylint: disable=unused-import 临时禁用警告

# ==================== 标准库 ====================
import datetime
import json
import traceback
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from backend.models.student_extension import StudentExtension  # 静态类型检查

# ==================== 第三方库 ====================
from sqlalchemy import case, literal, and_, or_, func
from flask import Blueprint, request, jsonify, current_app, send_file
import pandas as pd
from openpyxl import Workbook
from sqlalchemy.orm import Query

from backend.models.intervention import InterventionRecord
from backend.models.student_extension import StudentExtension
from backend.models.student import Student
from backend.config.config_manager import ConfigManager, config_manager
from backend.services.analysis_display_service import AnalysisDisplayService
from backend.infrastructure.database import db

def _get_field_type(field_name: str) -> str:
    """
    获取字段类型

    Args:
        field_name: 字段名

    Returns:
        str: 字段类型，默认 'category'
    """
    # 从全局配置中读取 METRIC_FIELDS
    metric_fields = config_manager.get_config("METRIC_FIELDS", {})
    return metric_fields.get(field_name, {}).get("type", "category")


def build_intervention_group_expr(selected_methods):
    """
    构建“干预方式”分组表达式，遵循全局三级配置驱动架构。
    使用 ConfigManager 从接口配置中获取映射，无硬编码。
    """
    # 从配置中心读取布尔字段映射：字段名 -> 显示名称
    boolean_map = config_manager.get_config('BOOLEAN_FIELDS_MAPPING', {})
    invert_map = {display: field for field, display in boolean_map.items()}

    whens = []
    for method in selected_methods:
        field_name = invert_map.get(method)
        if field_name:
            col = getattr(InterventionRecord, field_name)
            whens.append((col == True, literal(method)))
        else:
            current_app.logger.warning(f"未在配置中找到干预方式映射: {method}")

    expr = case(whens, else_=literal("其他")).label("intervention_methods_group")
    return expr


def _deduplicate_values(values):
    """
    去重函数，支持字典类型
    """
    if not values:
        return []
    
    # 如果都是简单类型（字符串、数字），直接使用set去重
    if all(isinstance(v, (str, int, float, bool)) for v in values):
        return list(set(values))
    
    # 如果包含字典，需要特殊处理
    unique_values = []
    for value in values:
        is_duplicate = False
        for existing in unique_values:
            if _values_equal(value, existing):
                is_duplicate = True
                break
        if not is_duplicate:
            unique_values.append(value)
    
    return unique_values

def _values_equal(val1, val2):
    """
    比较两个值是否相等，支持字典比较
    """
    # 类型不同，直接返回False
    if type(val1) != type(val2):
        return False
    
    # 如果是字典，比较内容
    if isinstance(val1, dict):
        if set(val1.keys()) != set(val2.keys()):
            return False
        for key in val1:
            if not _values_equal(val1[key], val2[key]):
                return False
        return True
    
    # 如果是列表，比较每个元素
    elif isinstance(val1, list):
        if len(val1) != len(val2):
            return False
        for i, item in enumerate(val1):
            if not _values_equal(item, val2[i]):
                return False
        return True
    
    # 其他类型直接比较
    else:
        return val1 == val2

# 移除重复的导入语句，已在文件顶部导入

analysis_api = Blueprint("analysis_api", __name__)

# 初始化配置管理器（遵循三层架构）
config_manager = ConfigManager()

# 初始化显示格式化服务
display_service = AnalysisDisplayService(config_manager)

# 🔧 统一配置：复用REPORT_ROLE_FIELD_CONFIG，避免重复配置
# 通过角色化字段配置获取统计指标字段，确保字段定义的一致性
REPORT_ROLE_CONFIG = config_manager.get_report_role_field_config()
METRIC_FIELDS = REPORT_ROLE_CONFIG.get("metric_fields", {})
GROUP_FIELDS = REPORT_ROLE_CONFIG.get("group_fields", {})
FILTER_FIELDS = REPORT_ROLE_CONFIG.get("filter_fields", {})

# 🔧 保留必要的独立配置（这些配置在REPORT_ROLE_FIELD_CONFIG中没有对应项）
FIXED_METRICS = config_manager.get_config("FIXED_METRICS", [])
INTERVENTION_SUBFIELDS = config_manager.get_config("INTERVENTION_SUBFIELDS", [])
CHART_COLORS = config_manager.get_config("CHART_COLORS", {})
FIELD_DISPLAY_MAPPING = config_manager.get_config("FIELD_DISPLAY_MAPPING", {})
complete_fields = config_manager.get_config("COMPLETE_FIELDS", [])


def _process_numeric_values(field_name, input_values, role, field_config=None):
    """
    标准化处理数值型查询条件
    返回结构：
    {
        "field": str,               # 原始字段名
        "role": str,                # 条件角色
        "valid_conditions": list,   # 有效条件列表
        "invalid_count": int,       # 无效数据计数
        "error_messages": list      # 错误信息明细
    }
    """
    result = {
        "field": field_name,
        "role": role,
        "valid_conditions": [],
        "invalid_count": 0,
        "error_messages": []
    }

    # 统一转为列表处理（兼容单值和多值）
    values_list = [input_values] if not isinstance(input_values, list) else input_values
    is_mixed_types = False

    for item in values_list:
        # 空值检查
        if item in (None, "", "null"):
            result["invalid_count"] += 1
            continue

        # 类型推断
        condition: dict = {"sql_ready": True}
        
        try:
            # 单值处理
            if isinstance(item, (int, float)):
                # 修复：正确处理数值类型
                if isinstance(item, int) or (isinstance(item, float) and item.is_integer()):
                    int_val = int(item)
                    condition["type"] = "single"
                    condition["value"] = int_val
                    condition["display_text"] = str(int_val)
                else:
                    float_val = float(item)
                    condition["type"] = "single"
                    condition["value"] = float_val
                    condition["display_text"] = f"{float_val:.2f}".rstrip('0').rstrip('.')
            
            # 🔧 新增：字符串单值处理
            elif isinstance(item, str):
                # 尝试解析为数值
                try:
                    parsed_val = _parse_single_value(item)
                    if parsed_val is not None:
                        condition["type"] = "single"
                        condition["value"] = parsed_val
                        condition["display_text"] = str(parsed_val)
                    else:
                        raise ValueError(f"无效的数值字符串: '{item}'")
                except ValueError as e:
                    raise ValueError(f"无法解析数值: '{item}' - {str(e)}")
            
            # 区间处理
            elif isinstance(item, dict):
                min_val = _parse_single_value(item.get("min"))
                max_val = _parse_single_value(item.get("max"))
                
                # 区间校验
                if min_val is None and max_val is None:
                    raise ValueError("区间至少需要min或max")
                if min_val is not None and max_val is not None and min_val > max_val:
                    raise ValueError(f"区间不合法: min({min_val}) > max({max_val})")

                if min_val is not None and max_val is not None and min_val == max_val:
                   # 🔧 当 min==max 时，生成单值
                   condition["type"] = "single"
                   condition["value"] = min_val
                   condition["display_text"] = str(min_val)
                else:
                   # 区间值保留原逻辑
                   condition["type"] = "range"
                   condition["value"] = {"min": min_val, "max": max_val}
                   condition["display_text"] = _generate_range_text(min_val, max_val)
            
            # 类型错误
            else:
                raise TypeError(f"不支持的类型: {type(item)}")

            result["valid_conditions"].append(condition)

        except (ValueError, TypeError) as e:
            result["invalid_count"] += 1
            result["error_messages"].append(f"字段[{field_name}]错误: {str(e)}")
            condition["sql_ready"] = False

    # 混合类型检查
    if len(set([c["type"] for c in result["valid_conditions"]])) > 1:
        result["error_messages"].insert(0, "禁止混合单值和区间条件")
        result["valid_conditions"] = []
        result["invalid_count"] = len(values_list)

    return result

def _parse_single_value(value):
    """解析单值并保持原始类型"""
    if value in (None, "", "null"):
        return None
    try:
        return int(value) if str(value).isdigit() else float(value)
    except (ValueError, TypeError):
        raise ValueError(f"无效数值: '{value}'")

def _generate_range_text(min_val, max_val):
    """生成区间显示文本"""
    # ▼▼▼ 修复：当 min_val 和 max_val 相同时，只显示一个值 ▼▼▼
    if min_val is not None and max_val is not None and min_val == max_val:
        return str(min_val)
    elif min_val is not None and max_val is not None:
        return f"{min_val}-{max_val}"
    elif min_val is not None:
        return f"≥{min_val}"
    else:
        return f"≤{max_val}"
    # ▲▲▲ 修复结束 ▲▲▲


def _parse_query_params(query_mode, template, advanced_str):
    # 基础结构初始化（兼容所有模式）
    params = {
        'metrics': [],
        'grouping': {'field': None, 'type': None},  # 初始化必填字段
        'filters': [],
        'raw_params': {
            'query_mode': query_mode,
            'template': template,
            'advanced_str': advanced_str
        },
        'error': None  # 新增错误标记
    }

    # 模式分支处理
    if query_mode == "template":
        # ===== 模板模式处理 =====
        if template == "template1":
            params['grouping'] = {
                 'field': 'age',
                 'type': 'number_range',
                 'label': '年龄',
                 'processed': {
                     'valid_conditions': [
                        {
                        'type': 'range',
                        'value': {'min': 6, 'max': 9},
                        'display_text': '6-9岁',
                        'sql_ready': True
                        },
                        {'type': 'range', 'value': {'min': 10, 'max': 12}, 'display_text': '10-12岁', 'sql_ready': True },
                    ],
                    'invalid_count': 0,
                    'error_messages': []
                }
            }
        elif template == "template2":
            params['grouping'] = {
                'field': 'gender',
                'type': 'category',
                'label': '性别',
                'selected': ['男', '女']  # 🔧 修复：使用selected而不是processed
            }        
            current_app.logger.debug(f"模板2分组配置: {params['grouping']}")
        # 固定模板指标 - 使用配置管理器获取字段显示名称
        vision_level_display_name = config_manager.get_report_field_display_name('vision_level')
        params['metrics'].append({
            'field': 'vision_level',
            'type': 'multi-select',
            'label': vision_level_display_name,
            'selected': ["临床前期近视", "轻度近视", "中度近视", "假性近视", "正常"],
            'include_other': True
        })

    elif query_mode == "custom":
        try:
            adv_conds = json.loads(advanced_str)
            
 # ▼▼▼ 新增：先收集所有分组条件 ▼▼▼
            group_values = {}
            for cond in adv_conds:
                if cond.get("role", "").lower() == "group":
                    field = cond.get("field")
                    value = cond.get("value")
                    
                    if field not in group_values:
                        group_values[field] = []
                    
                    # 🔧 修复：避免嵌套列表，直接合并值
                    if isinstance(value, list):
                        # 如果value是列表，直接扩展
                        group_values[field].extend(value)
                    else:
                        # 如果value是单个值，直接添加
                        group_values[field].append(value)
            
            # 🔧 修复：去重并记录调试信息
            for field, values in group_values.items():
                # 去重 - 支持字典类型
                unique_values = _deduplicate_values(values)
                group_values[field] = unique_values
                current_app.logger.debug(f"分组字段 {field} 的最终值: {unique_values}")
                
                # 🔧 调试：打印分组字段的详细信息
                current_app.logger.debug(f"分组字段 {field} 处理详情: 原始值={values}, 去重后={unique_values}")
            # ▲▲▲ 新增结束 ▲▲▲
            
            # 处理分组条件时保持与_apply_intervention_grouping一致
            for field, values in group_values.items():
                field_config = GROUP_FIELDS.get(field, {})
                field_type = field_config.get("type", "text")
                if field == "intervention_methods":
                    # 保持与_apply_intervention_grouping一致的参数结构
                    params['grouping'] = {
                        'field': field,
                        'type': field_type,
                        'selected': values,  # 直接使用values
                        'label': FIELD_DISPLAY_MAPPING.get(field, field)
                    }
                else:
                    _handle_grouping(params, field, values, field_type)

            # 处理其他条件
            for cond in adv_conds:
                role = cond.get("role", "").lower()
                if role != "group":
                    field = cond.get("field")
                    value = cond.get("value")
                    field_config = METRIC_FIELDS.get(field, {})
                    field_type = field_config.get("type", "text")
                    if role == "metric":
                        _handle_metric(params, field, value, field_type)
                    elif role == "filter":
                        _handle_filter(params, field, value, field_type)
                        
            return params
        except Exception as e:
            raise ValueError(f"解析高级条件失败: {str(e)}")
        
    return params  # 确保所有路径都有返回

# ========== 关键子函数 ==========

def _handle_grouping(params, field, values, field_type):
    """
    处理分组配置（兼容离散、多值及数值区间分组）：
      - 前端若传入 dict（含 min/max），统一当作数值区间分组；
      - 端点相等退化成单值；
      - 其他情况按离散分组处理，直接使用值列表。
    """
    # 1) 记录字段名
    params['grouping']['field'] = field

    # 2) 确保 values_list 是列表
    values_list = values if isinstance(values, list) else [values]

    # 3) 只要看到 dict，就当区间
    is_range_input = any(isinstance(v, dict) for v in values_list)
    if is_range_input:
        field_type = 'number_range'
    params['grouping']['type'] = field_type

    # 4) 根据全局配置决定 cast 类型（int/float）
    field_cfg = GROUP_FIELDS.get(field, {})
    data_type = field_cfg.get('type', 'int')  # 默认 int

    # —— 数值区间分组 —— 
    if field_type == 'number_range':
        processed = {
            "field": field,
            "role": "group",
            "valid_conditions": [],
            "invalid_count": 0,
            "error_messages": []
        }
        # a) 先走统一的数值解析
        for val in values_list:
            res = _process_numeric_values(
                field_name=field,
                input_values=val,
                role='group',
                field_config=field_cfg
            )
            processed["valid_conditions"].extend(res["valid_conditions"])
            processed["invalid_count"]   += res["invalid_count"]
            processed["error_messages"].extend(res["error_messages"])

        # b) 再做“原始值”到正确类型的转换
        for cond in processed["valid_conditions"]:
            if not cond.get("sql_ready", True):
                continue
            # 区间类型
            if cond["type"] == "range":
                raw = cond["value"]
                # 转 cast
                min_raw = raw.get("min")
                max_raw = raw.get("max")
                min_v = int(min_raw) if (min_raw is not None and data_type=='int') else (float(min_raw) if min_raw is not None else None)
                max_v = int(max_raw) if (max_raw is not None and data_type=='int') else (float(max_raw) if max_raw is not None else None)
                # 退化判断
                if min_v is not None and max_v is not None and min_v == max_v:
                    cond["type"] = "single"
                    cond["value"] = min_v
                    cond["display_text"] = str(min_v)
                else:
                    cond["value"] = {"min": min_v, "max": max_v}
                    cond["display_text"] = f"{min_v}-{max_v}"
            else:
                # 单值
                raw = cond["value"]
                v = int(raw) if data_type=='int' else float(raw)
                cond["value"] = v
                cond["display_text"] = str(v)

        params['grouping']['processed'] = processed

    # —— 离散分组 —— 
    else:
        params['grouping']['selected'] = values_list
        current_app.logger.debug(f"离散分组值: {values_list}")

def _handle_metric(params, field, value, field_type):
    """
    处理统计指标配置：
      - 数值区间指标：只要前端传 dict（或列表里有 dict），就调用 _process_numeric_values，
        支持多个区间及单值，并把 raw value 统一转成 float／{min: float, max: float}，端点相等时退化为单值；
      - 离散/多选指标：否则统一转为列表 selected。
    """
    # 1. 获取显示名称
    label = config_manager.get_report_field_display_name(field)
    metric = {'field': field, 'label': label}

    # 2. 判断是否走数值区间／单值逻辑
    is_range_input = isinstance(value, dict) \
                     or (isinstance(value, list) and any(isinstance(v, dict) for v in value))

    if is_range_input:
        processed = {
            "field": field, "role": "metric",
            "valid_conditions": [], "invalid_count": 0, "error_messages": []
        }
        vals = value if isinstance(value, list) else [value]

        for v in vals:
            sub = _process_numeric_values(
                field_name=field,
                input_values=v,
                role='metric',
                field_config=METRIC_FIELDS.get(field, {})
            )
            # —— 统一把 sub["valid_conditions"] 中 raw value 转成数字 —— 
            for cond in sub["valid_conditions"]:
                if cond["type"] == "range":
                    raw = cond["value"]
                    min_val = float(raw["min"])
                    max_val = float(raw["max"])
                    if min_val == max_val:
                        # 端点相等 → 退化为单值
                        cond["type"] = "single"
                        cond["value"] = min_val
                        cond["display_text"] = str(min_val)
                    else:
                        # 真正区间
                        cond["value"] = {"min": min_val, "max": max_val}
                        cond["display_text"] = _generate_range_text(min_val, max_val)
                else:
                    # 单值
                    cond["value"] = float(cond["value"])
                    cond["display_text"] = str(cond["value"])

            # 合并到 processed
            processed["valid_conditions"].extend(sub["valid_conditions"])
            processed["invalid_count"]   += sub["invalid_count"]
            processed["error_messages"].extend(sub["error_messages"])

        metric['processed'] = processed
    else:
        # 离散/多选指标，直接转列表
        metric['selected'] = value if isinstance(value, list) else [value]

    params['metrics'].append(metric)
    current_app.logger.debug(f"添加统计指标: {metric}")

def _handle_filter(params, field, value, field_type):
    """处理筛选条件"""
    filter_cond = {
        'field': field,
        'type': field_type,
        'operator': '=',  # 默认操作符
        'value': None
    }
    
    # 🔧 修复：根据字段类型正确处理值
    field_cfg = FILTER_FIELDS.get(field, {})
    if field_cfg.get("supports_range") and isinstance(value, dict):
        processed = _process_numeric_values(
            field_name=field,
            input_values=value,
            role='filter',
            field_config=FILTER_FIELDS.get(field, {})
        )
        filter_cond['value'] = processed['valid_conditions']
    elif field_type == "number_input":
        # number_input 不走复杂逻辑，直接用值
        filter_cond['value'] = value
    else:
        filter_cond['value'] = value
    
    params['filters'].append(filter_cond)

def get_column_by_field(field):
    """
    根据字段名获取对应的数据库列对象
    支持新的数据模型结构：VisionRecord, InterventionRecord, FollowupRecord
    🔧 修复：智能处理多表字段，避免笛卡尔积
    """
    # ▼▼▼ 新增：调试日志 ▼▼▼
    current_app.logger.debug(f"获取字段列: {field}")
    # ▲▲▲ 新增结束 ▲▲▲

    # 导入新的数据模型
    from backend.models.vision import VisionRecord
    from backend.models.intervention import InterventionRecord
    from backend.models.followup import FollowupRecord

    if field == "intervention_methods":
        return literal(1).label("intervention_methods")
    
    # 🔧 修复：按优先级顺序查找字段，支持所有表
    # 1. StudentExtension 中的扩展字段（最高优先级）
    if hasattr(StudentExtension, field):
        return getattr(StudentExtension, field)
    
    # 2. Student 中的基本字段
    if hasattr(Student, field):
        return getattr(Student, field)

    # 3. VisionRecord 中的视力相关字段
    try:
        if hasattr(VisionRecord, field):
            # 🔧 修复：使用子查询避免笛卡尔积
            return _get_vision_field(field)
    except Exception as e:
        current_app.logger.warning(f"获取VisionRecord字段 '{field}' 失败: {e}")
    
    # 4. InterventionRecord 中的干预相关字段
    try:
        if hasattr(InterventionRecord, field):
            return _get_intervention_field(field)
    except Exception as e:
        current_app.logger.warning(f"获取InterventionRecord字段 '{field}' 失败: {e}")
    
    # 5. FollowupRecord 中的随访相关字段
    try:
        if hasattr(FollowupRecord, field):
            return _get_followup_field(field)
    except Exception as e:
        current_app.logger.warning(f"获取FollowupRecord字段 '{field}' 失败: {e}")
    
    # 🔧 修复：如果字段不存在，返回None而不是抛出异常
    current_app.logger.warning(f"字段 '{field}' 不存在于任何表中，返回None")
    return literal(None).label(f"{field}_none")


def _get_vision_field(field):
    """
    获取视力记录表的字段，直接返回字段对象
    
    🔧 修复：直接返回字段对象，避免子查询导致的auto-correlation错误
    📋 核心作用：返回VisionRecord表中的字段，由调用方处理JOIN
    
    Args:
        field: 字段名
        
    Returns:
        SQLAlchemy列表达式
    """
    from backend.models.vision import VisionRecord
    
    # 检查字段是否存在于VisionRecord模型中
    if not hasattr(VisionRecord, field):
        current_app.logger.warning(f"字段 '{field}' 不存在于VisionRecord模型中")
        return literal(None).label(f"{field}_none")
    
    # 直接返回字段对象
    return getattr(VisionRecord, field)


def _get_intervention_field(field):
    """
    获取干预记录表的字段，直接返回字段对象
    
    🔧 修复：直接返回字段对象，避免子查询导致的auto-correlation错误
    📋 核心作用：返回InterventionRecord表中的字段，由调用方处理JOIN
    
    Args:
        field: 字段名
        
    Returns:
        SQLAlchemy列表达式
    """
    from backend.models.intervention import InterventionRecord
    
    # 检查字段是否存在于InterventionRecord模型中
    if not hasattr(InterventionRecord, field):
        current_app.logger.warning(f"字段 '{field}' 不存在于InterventionRecord模型中")
        return literal(None).label(f"{field}_none")
    
    # 直接返回字段对象
    return getattr(InterventionRecord, field)


def _get_followup_field(field):
    """
    获取随访记录表的字段，直接返回字段对象
    
    🔧 修复：直接返回字段对象，避免子查询导致的auto-correlation错误
    📋 核心作用：返回FollowupRecord表中的字段，由调用方处理JOIN
    
    Args:
        field: 字段名
        
    Returns:
        SQLAlchemy列表达式
    """
    from backend.models.followup import FollowupRecord
    
    # 检查字段是否存在于FollowupRecord模型中
    if not hasattr(FollowupRecord, field):
        current_app.logger.warning(f"字段 '{field}' 不存在于FollowupRecord模型中")
        return literal(None).label(f"{field}_none")
    
    # 直接返回字段对象
    return getattr(FollowupRecord, field)


def aggregate_query_data(query, query_mode, template, data_year,advanced_str):
    # ===== 保留原始全局导入 =====
    global StudentExtension
    from backend.models.student_extension import StudentExtension as _StudentExtension
    StudentExtension = _StudentExtension
    # ===== 保留结束 =====

    # ===== 保留原始初始化语句 =====
    grouping_cols = []
    metric_conditions = []
    filter_conditions = []
    free_group_field = None
    group_singles = {}
    group_intervals = {}
    dynamic_metrics = []
    # ===== 保留结束 =====
    
    try:
        # 1. 参数解析
        parsed_params = _parse_query_params(query_mode, template, advanced_str)

        # 2. 根据查询类型构建正确的基础查询
        if query is None:
            # 如果query为None，直接构建VisionRecord查询
            from backend.models.vision import VisionRecord
            query = db.session.query(VisionRecord).join(Student).join(StudentExtension)
        elif hasattr(query.column_descriptions[0]['type'], '__tablename__'):
            base_table = query.column_descriptions[0]['type'].__tablename__
            if base_table == 'student_extensions':
                # 从StudentExtension查询转换为VisionRecord查询
                from backend.models.vision import VisionRecord
                query = db.session.query(VisionRecord).join(Student).join(StudentExtension)

        # ▼▼▼ 重新设计：正确的数据筛选逻辑 ▼▼▼
 # 1. 年份筛选 - 始终基于 measure_date 与 data_year
        from backend.models.vision import VisionRecord
        from sqlalchemy import func
 
         # data_year 参数来自函数签名：如果前端传了就用它，否则取扩展表的最新年份
        if data_year:
             filter_year = int(data_year)
        else:
             filter_year = db.session.query(func.max(StudentExtension.data_year)).scalar()
 
         # 应用到视力表的 measure_date 和 扩展表的 data_year
        query = query.filter(
            func.year(VisionRecord.measure_date) == filter_year,
            StudentExtension.data_year == filter_year
        )
        # ===== 新增：干预记录子查询并关联 =====
        from backend.models.intervention import InterventionRecord
        # 复用上面导入的 func
        subq = (
            db.session.query(
                InterventionRecord.student_id.label("sid"),
                # MySQL 用 GROUP_CONCAT(DISTINCT ...)
                func.group_concat(InterventionRecord.intervention_type.distinct())
                    .label("types")
            )
            .filter(InterventionRecord.data_year == filter_year)
            .group_by(InterventionRecord.student_id)
            .subquery()
        )

        # 左外连接子查询，后面可用 subq.c.types
        query = query.outerjoin(subq, subq.c.sid == Student.id)
        current_app.logger.debug("关联干预记录子查询，聚合字段 types")
        # 将 subq 传给后续分组使用
        parsed_params['subq'] = subq
        # ===== 新增结束 =====
        # 3. 最新记录筛选（略…）
        # 4. 应用剩余 filters、分组、指标、执行查询… 

        
        # 2. 在年份范围内选择最新记录
        has_measurement_filter = False
        for cond in parsed_params.get('filters', []):
            if cond.get('field') == 'measurement_sequence':
                has_measurement_filter = True
                break
        
        if not has_measurement_filter:
            # 在年份范围内选择每个学生的最新记录
            current_app.logger.info("应用最新记录筛选：在年份范围内选择每个学生的最新记录")
            # 如果没有指定年份，使用学生扩展表中的最近年份
            if not data_year:
                data_year = db.session.query(func.max(StudentExtension.data_year)).scalar()
            query = _apply_latest_measurement_filter_by_year(query, data_year)
        else:
            current_app.logger.info("检测到测量序号筛选条件，使用用户指定的筛选条件")
        
        # 3. 无效数据过滤
        grouping_field = parsed_params['grouping'].get('field')
        metric_fields = [metric['field'] for metric in parsed_params['metrics']]

        invalid_conditions = []
        # 仅当分组字段不是“干预方式”时，才做无效数据过滤
        if grouping_field and grouping_field not in ("intervention_methods", "intervention_type"):
            # 分组字段本身不为空
            col = get_column_by_field(grouping_field)
            invalid_conditions.append(col.isnot(None))
            if hasattr(col.type, 'python_type') and col.type.python_type == str:
                invalid_conditions.append(col != '')
            # 指标字段也要过滤
            for mfield in metric_fields:
                mcol = get_column_by_field(mfield)
                invalid_conditions.append(mcol.isnot(None))
                if hasattr(mcol.type, 'python_type') and mcol.type.python_type == str:
                    invalid_conditions.append(mcol != '')

        invalid_count = 0
        if invalid_conditions:
            invalid_query = query.filter(~and_(*invalid_conditions))
            invalid_count = invalid_query.count()
            current_app.logger.info(f"过滤无效数据: {invalid_count} 条")
            query = query.filter(*invalid_conditions)
        # ▲▲▲ 重新设计结束 ▲▲▲

        # 3. 应用过滤条件
        query = _apply_filters(parsed_params, query)
        
        # 4. 应用分组
        query, group_title, grouping_expr = _apply_grouping(parsed_params, query)
        current_app.logger.debug(f"分组字段: {group_title}, 表达式: {grouping_expr}")
        
        # 5. 应用统计指标（传入分组表达式）
        query = _apply_metrics(parsed_params, query, grouping_expr)
        
        # 6. 执行查询并格式化结果
        records = query.all()
        result = display_service.format_table_data(
            params={'metrics': parsed_params['metrics'],
                'raw_params': parsed_params['raw_params'],
                'grouping_expr': grouping_expr},
            records=records,
            group_title=group_title
        )
        
        # ▼▼▼ 新增：将无效数据数量添加到返回结果中 ▼▼▼
        result["invalid_count"] = invalid_count
        # ▲▲▲ 新增结束 ▲▲▲

        # ===== 保留原始调试日志 =====
        # ===== 保留结束 =====

        return result

    except Exception as e:
        current_app.logger.error(f"聚合查询失败: {str(e)}")
        raise


def _apply_latest_measurement_filter(query):
    """
    应用最新测量数据筛选，确保每个学生只统计一条有效记录
    默认选择每个学生最后一次检查的有效数据（measurement_sequence最大的有效记录）
    
    Args:
        query: 已经过滤了无效数据的基础查询对象
        
    Returns:
        更新后的查询对象，只包含每个学生的最新有效记录
    """
    from backend.models.vision import VisionRecord
    from sqlalchemy import func
    
    # 使用子查询获取每个学生的最新测量序号（只在有效数据中查找）
    latest_sequence_subquery = db.session.query(
        VisionRecord.student_id,
        func.max(VisionRecord.measurement_sequence).label('max_sequence')
    ).filter(
        VisionRecord.vision_level.isnot(None)  # 确保只查找有效数据
    ).group_by(VisionRecord.student_id).subquery()
    
    # 应用筛选条件：只查询每个学生的最新有效记录
    query = query.join(
        latest_sequence_subquery,
        and_(
            VisionRecord.student_id == latest_sequence_subquery.c.student_id,
            VisionRecord.measurement_sequence == latest_sequence_subquery.c.max_sequence
        )
    )
    
    current_app.logger.info("已应用最新测量数据筛选，确保每个学生只统计一条有效记录")
    return query


def _apply_latest_measurement_filter_by_year(query, year):
    """
    在指定年份内选择每个学生的最新记录
    基于measurement_sequence字段，在指定年份的范围内选择最大值
    
    Args:
        query: 基础查询对象（已经应用了年份筛选）
        year: 指定的年份
        
    Returns:
        筛选后的查询对象
    """
    from backend.models.vision import VisionRecord
    from sqlalchemy import func, and_
    
    # 🔧 修复：使用窗口函数而不是子查询，确保继承基础查询的所有条件
    # 在已经应用年份筛选的查询基础上，使用窗口函数选择每个学生的最新记录
    # 注意：这里不需要重新应用年份筛选，因为基础查询已经包含了所有筛选条件
    subquery = query.with_entities(
        VisionRecord.student_id,
        VisionRecord.id,
        func.row_number().over(
            partition_by=VisionRecord.student_id,
            order_by=VisionRecord.measurement_sequence.desc()
        ).label('rn')
    ).subquery()
    
    # 只选择每个学生的第一条记录（最新记录）
    query = query.join(
        subquery,
        and_(
            VisionRecord.id == subquery.c.id,
            subquery.c.rn == 1
        )
    )
    
    current_app.logger.info(f"已应用{year}年最新记录筛选，确保每个学生只统计一条有效记录")
    return query


def _apply_filters(params, query):
    """
    应用过滤条件（完整保留原有过滤逻辑）
    参数:
        params: 包含过滤条件的参数字典
        query: 已经包含基础过滤条件（如年份）的查询对象
    返回:
        更新后的查询对象
    """
    # 调试：记录原始过滤条件
    current_app.logger.debug(f"【过滤前】已有条件: {query.whereclause}")
    
    # 初始化新过滤条件列表
    new_conditions = []
    
    # 1. 处理固定模板的过滤条件
    if params['raw_params']['query_mode'] == "template":
        # 固定模板可能需要添加其他过滤条件
        pass
    
    # 2. 处理自定义模式的过滤条件
    for cond in params['filters']:
        field = cond['field']
        operator = cond.get('operator', '=')
        value = cond['value']
        
        # 🔧 修复：跳过data_year字段，因为已经在前面处理了
        if field == 'data_year':
            current_app.logger.debug(f"跳过data_year字段，已在前面处理")
            continue
        
        # 获取字段对应的列
        col = get_column_by_field(field)
        
        # 🔧 修复：检查字段类型，正确处理日期字段
        field_type = _get_field_type(field)
        current_app.logger.debug(f"字段 {field} 类型: {field_type}, 值: {value}")
        
        # 根据操作符构建条件
        if operator == "=":
            # 处理=操作符 - 取数组第一个元素
            if isinstance(value, list) and len(value) > 0:
                processed_value = _process_field_value(value[0], field_type)
                new_conditions.append(col == processed_value)
            else:
                processed_value = _process_field_value(value, field_type)
                new_conditions.append(col == processed_value)
                
        elif operator == "like":
            # 处理like操作符 - 支持数组
            if field_type == "date":
                # 🔧 修复：日期字段不支持like操作，转换为日期范围查询
                current_app.logger.warning(f"日期字段 {field} 不支持like操作，跳过该条件")
                continue
            elif field_type == "int" or field_type == "number_input":
                # 🔧 修复：数字字段不支持like操作，转换为等值查询
                current_app.logger.warning(f"数字字段 {field} 不支持like操作，转换为等值查询")
                if isinstance(value, list) and len(value) > 0:
                    processed_value = _process_field_value(value[0], field_type)
                    new_conditions.append(col == processed_value)
                else:
                    processed_value = _process_field_value(value, field_type)
                    new_conditions.append(col == processed_value)
            elif isinstance(value, list):
                like_conditions = [col.like(f"%{v}%") for v in value]
                new_conditions.append(or_(*like_conditions))
            else:
                new_conditions.append(col.like(f"%{value}%"))
                
        elif operator == "in":
            # 处理in操作符
            if field_type == "date":
                # 🔧 修复：日期字段的in操作需要转换日期格式
                processed_values = [_process_field_value(v, field_type) for v in value]
                new_conditions.append(col.in_(processed_values))
            else:
                new_conditions.append(col.in_(value))
            
        elif operator == ">":
            # 处理>操作符
            if field_type == "date":
                processed_value = _process_field_value(value, field_type)
                new_conditions.append(col > processed_value)
            else:
                new_conditions.append(col > float(value))    
            
        elif operator == ">=":
            # 处理>=操作符
            if field_type == "date":
                processed_value = _process_field_value(value, field_type)
                new_conditions.append(col >= processed_value)
            else:
                new_conditions.append(col >= float(value))
            
        elif operator == "<":
            # 处理<操作符
            if field_type == "date":
                processed_value = _process_field_value(value, field_type)
                new_conditions.append(col < processed_value)
            else:
                new_conditions.append(col < float(value))
            
        elif operator == "<=":
            # 处理<=操作符
            if field_type == "date":
                processed_value = _process_field_value(value, field_type)
                new_conditions.append(col <= processed_value)
            else:
                new_conditions.append(col <= float(value))    
    # 3. 应用新条件（不影响已有条件）
    if new_conditions:
        query = query.filter(and_(*new_conditions))
        current_app.logger.debug(f"【新增条件】: {new_conditions}")
    
    # 调试：查看最终过滤条件
    current_app.logger.debug(f"【过滤后】完整条件: {query.whereclause}")
    return query

def _apply_grouping(params, query):
    """
    应用分组逻辑：
      - 干预方式分组：CASE WHEN based on subq.c.types，并立即做 with_entities + group_by。
      - 其它字段分组：直接返回分组表达式，由 _apply_metrics 统一处理聚合。
    """
    from sqlalchemy import case, literal, and_, or_, func
    # 取出干预子查询
    subq = params.get('subq')
    if subq is None:
        raise ValueError("缺少干预记录子查询，请检查 aggregate_query_data 是否正确设置 subq")

    grouping = params.get('grouping', {})
    field = grouping.get('field')
    if not field:
        raise ValueError("分组配置必须包含 'field'")

    # ——— 专用：干预方式分组 ———
    if field in ("intervention_methods", "intervention_type"):
        selected = grouping.get('selected', [])
        whens = []

        # 1. 单项：m 在 types 且其它选项不在 types
        for m in selected:
            whens.append((
                and_(
                    func.find_in_set(m, subq.c.types) > 0,
                    *[func.find_in_set(o, subq.c.types) == 0 for o in selected if o != m]
                ), m
            ))
        # 2. 组合：两两、三三……
        from itertools import combinations
        for r in range(2, len(selected) + 1):
            for combo in combinations(selected, r):
                whens.append((
                    and_(
                        *[func.find_in_set(x, subq.c.types) > 0 for x in combo],
                        *[func.find_in_set(o, subq.c.types) == 0 for o in selected if o not in combo]
                    ), "+".join(combo)
                ))
        # 3. “其他干预方式”：types 非空，且不含任何选中项
        whens.append((
            and_(
                subq.c.types != None,
                *[func.find_in_set(m, subq.c.types) == 0 for m in selected]
            ), "其他干预方式"
        ))
        # 4. “未选择任何干预方式”
        whens.append((
            or_(subq.c.types == None, subq.c.types == ""),
            "未选择任何干预方式"
        ))

        grouping_expr = case(*whens, else_=literal("未知")).label(f"{field}_group")
        title = config_manager.get_config("FIELD_DISPLAY_MAPPING", {}) \
                    .get("intervention_methods", "干预方式")

        # 重新选列：分组列 + 原指标列
        orig = grouping_expr.name
        cols = [grouping_expr] + [
            d['expr'] for d in query.column_descriptions if d['name'] != orig
        ]
        return query.with_entities(*cols).group_by(grouping_expr), title, grouping_expr

    # ——— 其余字段走通用分组（由 _apply_metrics 统一执行聚合） ———
    # 拿到对应列和标题，返回原 query
    from backend.api.analysis_api import get_column_by_field
    col = get_column_by_field(field)
    title_map = config_manager.get_config("FIELD_DISPLAY_MAPPING", {})
    group_title = grouping.get('label') or title_map.get(field, field)

    # 数值区间分组
    if grouping.get('processed', {}).get('valid_conditions'):
        cases = []
        for cond in grouping['processed']['valid_conditions']:
            if not cond.get('sql_ready', True):
                continue
            if cond['type'] == 'range':
                checks = []
                v = cond['value']
                if v.get('min') is not None: checks.append(col >= v['min'])
                if v.get('max') is not None: checks.append(col <= v['max'])
                expr = and_(*checks) if len(checks) > 1 else checks[0]
                cases.append((expr, literal(cond['display_text'])))
            else:  # single
                cases.append((col == cond['value'], literal(cond['display_text'])))
        grouping_expr = case(*cases, else_=literal("其他")).label(f"{field}_group")
    else:
        # 离散分组
        vals = grouping.get('selected') or []
        if vals:
            cases = [(col == v, literal(str(v))) for v in vals]
            grouping_expr = case(*cases, else_=literal("其他")).label(f"{field}_group")
        else:
            grouping_expr = col

    return query, group_title, grouping_expr

def _apply_intervention_grouping(params, query):
    """
    干预方式分组：
      - 用户选了哪些干预 display_name，就映射到对应的子字段列名；
      - CASE WHEN 严格匹配子字段计数 > 0 且其它 = 0；
      - 先“其他组合”(含未选中)；再“用户所有组合”；最后“未选任何”。
      - label = f"{field}_group"，title 从 FIELD_DISPLAY_MAPPING 里取。
    """
    from sqlalchemy import case, literal, and_, or_
    from itertools import combinations

    grouping = params['grouping']
    field = grouping['field']  # 'intervention_type' 或 'intervention_methods'
    selected_display = grouping.get('selected', []) or []

    # 1) 构造 display_name -> actual_field 列名的反向映射
    #    INTERVENTION_SUBFIELDS = ['guasha_count','aigiu_count','erxue_yawan_count', …]
    #    FIELD_DISPLAY_MAPPING maps each of those to Chinese
    DISPLAY_TO_FIELD = {
        FIELD_DISPLAY_MAPPING[col]: col
        for col in INTERVENTION_SUBFIELDS
    }

    # 2) 把前端传的中文列表映射为真正的列名列表
    selected_fields = [
        DISPLAY_TO_FIELD.get(name)
        for name in selected_display
        if name in DISPLAY_TO_FIELD
    ]

    # 3) 生成所有非空子集组合
    combos = []
    for r in range(1, len(selected_fields)+1):
        for subset in combinations(selected_fields, r):
            combos.append(subset)

    cases = []

    # 4) “其他干预方式组合”：任何一个不在 selected_fields 里的列 > 0
    other_fields = [c for c in INTERVENTION_SUBFIELDS if c not in selected_fields]
    if other_fields:
        cond_other = or_(*[get_column_by_field(c) > 0 for c in other_fields])
        cases.append((cond_other, literal("其他干预方式组合")))

    # 5) 用户选中组合：subset 全部 >0，且其余全 <=0
    for subset in combos:
        pos = and_(*[get_column_by_field(c) > 0 for c in subset])
        neg = and_(*[get_column_by_field(c) <= 0 for c in INTERVENTION_SUBFIELDS if c not in subset])
        # label 用中文 display_name 拼接
        labels = "+".join([FIELD_DISPLAY_MAPPING[c] for c in subset])
        cases.append((and_(pos, neg), literal(labels)))

    # 6) “未选择任何干预方式”：所有子字段都 <= 0
    none_cond = and_(*[get_column_by_field(c) <= 0 for c in INTERVENTION_SUBFIELDS])
    cases.append((none_cond, literal("未选择任何干预方式")))

    # 7) 构造 CASE、label 和 title
    group_col = case(*cases, else_=literal("未知")) \
                    .label(f"{field}_group")
    title_map = config_manager.get_config("FIELD_DISPLAY_MAPPING", {})
    group_title = title_map.get(field, "干预方式")

    # 8) 将 group_col 注入查询并做 group_by
    #    不改变原有 columns，只在最外层聚合时扣分组列
    return query.group_by(group_col), group_title, group_col

def _apply_metrics(params, query, grouping_expr=None):
    """
    应用统计指标：遍历 params['metrics']，对数值区间/单值 和 分类 两种场景分别生成 SUM(CASE…) 列
    """
    # 1. 分组表达式
    if grouping_expr is None:
        grouping_expr = get_column_by_field(params['grouping']['field'])

    # 2. 基础查询列
    from backend.models.vision import VisionRecord
    from sqlalchemy import literal, and_, case
    columns = [
        grouping_expr,
        func.count(VisionRecord.student_id).label("total_count")
    ]

    # 3. 各指标追加
    for metric in params.get('metrics', []):
        col = get_column_by_field(metric['field'])

        # 3.1 数值区间／单值
        for cond in metric.get('processed', {}).get('valid_conditions', []):
            if not cond.get('sql_ready', True):
                continue

            if cond['type'] == 'range':
                min_v = float(cond['value']['min'])
                max_v = float(cond['value']['max'])
                expr = and_(col >= literal(min_v), col <= literal(max_v))
                suffix = f"{min_v}_{max_v}_count"
            else:
                v = float(cond['value'])
                expr = (col == literal(v))
                suffix = f"{v}_count"

            columns.append(
                func.sum(case((expr, 1), else_=0)).label(f"{metric['field']}_{suffix}")
            )

        # 3.2 分类统计（当 processed 为空时）
        if not metric.get('processed', {}).get('valid_conditions'):
            for v in metric.get('selected', []):
                columns.append(
                    func.sum(case((col == v, 1), else_=0)).label(f"{metric['field']}_{v}_count")
                )

    # 4. 返回新的查询
    return query.with_entities(*columns).group_by(grouping_expr)

def _process_field_value(value, field_type):
    """
    根据字段类型处理值
    
    Args:
        value: 原始值
        field_type: 字段类型
        
    Returns:
        处理后的值
    """
    if value is None:
        return None
        
    try:
        # 🔧 修复：检查值是否已经被处理过（字典格式）
        if isinstance(value, dict):
            # 如果值已经是处理过的字典格式，直接提取value字段
            if 'value' in value:
                return value['value']
            else:
                current_app.logger.warning(f"处理过的值缺少value字段: {value}")
                return None
        
        if field_type == "date":
            # 处理日期类型
            if isinstance(value, str):
                # 尝试解析日期字符串
                if value.isdigit():
                    # 如果是纯数字，可能是月份或日期，跳过
                    current_app.logger.warning(f"日期字段收到无效值: {value}，跳过该条件")
                    return None
                elif len(value) == 4 and value.isdigit():
                    # 如果是4位数字，可能是年份，转换为日期范围
                    current_app.logger.warning(f"日期字段收到年份值: {value}，跳过该条件")
                    return None
                else:
                    # 尝试解析日期格式
                    try:
                        from datetime import datetime
                        # 尝试多种日期格式
                        for fmt in ['%Y-%m-%d', '%Y/%m/%d', '%Y-%m-%d %H:%M:%S']:
                            try:
                                return datetime.strptime(value, fmt).date()
                            except ValueError:
                                continue
                        # 如果都失败，记录警告并跳过
                        current_app.logger.warning(f"无法解析日期值: {value}，跳过该条件")
                        return None
                    except Exception as e:
                        current_app.logger.error(f"日期解析失败: {value}, 错误: {e}")
                        return None
            elif hasattr(value, 'date'):
                # 如果是datetime对象，提取日期部分
                return value.date()
            else:
                return value
        elif field_type == "int":
            # 处理整数类型
            return int(value)
        elif field_type == "float":
            # 处理浮点数类型
            return float(value)
        else:
            # 其他类型直接返回
            return value
            
    except Exception as e:
        current_app.logger.error(f"处理字段值失败: {value}, 类型: {field_type}, 错误: {e}")
        return value